
#include "cache_lines.h"


namespace	memory
{
	cache_lines::cache_lines()
		: listener_( NULL )
		, lookaside_( NULL )
		, count_( 0 )
	{
	}

	cache_lines::~cache_lines()
	{
		destroy();
	}

	bool	cache_lines::create( cache_line_listener* listener, size_t parallelism, size_t lookaside_count )
	{
		assert( parallelism > 0 );
		assert( lookaside_ == NULL );
		if( parallelism <= 0 || lookaside_ != NULL )
		{
			return false;
		}

		lookaside_ = new lookaside[parallelism];
		if( lookaside_ == NULL )
		{
			return false;
		}

		for( size_t i = 0; i < parallelism; ++i )
		{
			if( lookaside_[i].create( lookaside_count, max_lookaside_cache_count ) == false )
			{
				return false;
			}
		}

		count_ = parallelism;
		listener_ = listener;
		return true;
	}

	void	cache_lines::destroy()
	{
		clear();

		count_ = 0;
		if( lookaside_ != NULL )
		{
			delete [] lookaside_;
			lookaside_ = NULL;
		}

		listener_ = NULL;
	}

	SLIST_ENTRY*	cache_lines::pop( size_t line_idx, size_t las_idx )
	{
		SLIST_ENTRY* entry = NULL;
		lookaside* las = at( line_idx );
		if( las != NULL )
		{
			entry = las->pop( las_idx );
			if( entry != NULL )
			{
				entry->Next = NULL;
			}
		}

		return entry;
	}

	bool	cache_lines::push( size_t line_idx, size_t las_idx, SLIST_ENTRY* entry )
	{
		if( entry != NULL )
		{
			assert( entry->Next == NULL );

			lookaside* las = at( line_idx );
			if( las != NULL && las->push( las_idx, entry ) == true )
			{
				return true;
			}
		}

		return false;
	}

	size_t	cache_lines::line_count()	const
	{
		return count_;
	}

	size_t	cache_lines::las_count()	const
	{
		lookaside* las = at( 0 );
		if( las != NULL )
		{
			return las->size();
		}

		return 0;
	}

	lookaside*	cache_lines::at( size_t line_idx )	const
	{
		lookaside* las = NULL;
		if( lookaside_ != NULL )
		{
			if( 0 <= line_idx && line_idx < count_ )
			{
				las = &lookaside_[line_idx];
			}
		}

		return las;
	}

	void	cache_lines::clear()
	{
		if( lookaside_ != NULL )
		{
			for( size_t line_idx = 0; line_idx < count_; ++line_idx )
			{
				lookaside* las = at( line_idx );
				if( las != NULL )
				{
					for( size_t las_idx = 0; las_idx < las->size(); ++las_idx )
					{
						SLIST_ENTRY* first = las->pop_all( las_idx );
						while( first != NULL )
						{
							SLIST_ENTRY* temp = first;
							first = first->Next;

							if( listener_ != NULL )
							{
								listener_->on_delete( temp );
							}
						}
					}
				}
			}
		}
	}

}
